#include <arpa/inet.h>
#include <string.h>

#include "RtpPacket.hpp"
#include "RtpPacketUtil.hpp"

#include "HiCommonLog.hpp"


static char error_script[512] = {0};

namespace Hi {
static bool IsRtp(const uint8_t *data, size_t len);
static RtpPacket* createRtpPacket(RtpPacket::Header *header, 
	RtpPacket::ExtensionHeader *extensionHeader,
	uint8_t *payload, bool parsePadding, 
	size_t len, size_t readed_len);
static bool checkCsrc(RtpPacket::Header *header, uint8_t** ptr, size_t len, size_t& readed_len);
static RtpPacket::ExtensionHeader* readExtensionHeaderWhenExist(RtpPacket::Header *header,
	uint8_t** ptr, size_t len, size_t& readed_len) ;

static void writeErrror(const char* error) {
	memcpy(error_script, error, strlen(error));
}

const char* RtpPacketUtil::Error() {
	return error_script;
}
RtpPacket *RtpPacketUtil::Parse(const uint8_t *data, size_t len, bool parsePadding /*=true*/) {
	::memset(error_script, 0, sizeof(error_script));
	if (!IsRtp(data, len)) {
		writeErrror("this is not a rtp packet");
		return nullptr;
	}

	auto *ptr = const_cast<uint8_t *>(data);

	// Get the header.
	RtpPacket::Header *header = reinterpret_cast<RtpPacket::Header *>(ptr);

	// Inspect data after the minimum header size.
	ptr += sizeof(RtpPacket::Header);
	size_t readed_len = sizeof(RtpPacket::Header);
	HI_DEBUG("read head,len:%d,padding:%d,csrcCount:%d,version:%d,paloadType:%d", 
		readed_len, header->padding, header->csrcCount, header->version, header->payloadType);

	// Check CSRC list.
	bool ret = checkCsrc(header, &ptr, len, readed_len);
	if (!ret) {
		return nullptr;
	}
	HI_DEBUG("read csrc, len:%d", readed_len);

	// Check header extension.
	RtpPacket::ExtensionHeader* extensionHeader{nullptr};
	if (header->extension != 0u) {
		extensionHeader = readExtensionHeaderWhenExist(header, &ptr, len, readed_len);
		if (nullptr == extensionHeader) {
			return nullptr;
		}
	}

	// Get payload.
	uint8_t *payload = ptr;
	return createRtpPacket(header, extensionHeader, payload, parsePadding, len, readed_len);
}

static bool checkCsrc(RtpPacket::Header *header, uint8_t** ptr, size_t len, size_t& readed_len) {
	if (0u == header->csrcCount) {
		return true;
	}
	size_t csrcListSize = header->csrcCount * sizeof(header->ssrc);

	// Packet size must be >= header size + CSRC list.
	if (len < readed_len + csrcListSize) {
		writeErrror("not enough space for the announced CSRC list, packet discarded");
		return false;
	}
	*ptr += csrcListSize;
	readed_len += csrcListSize;
	return true;
}

static RtpPacket::ExtensionHeader* readExtensionHeaderWhenExist(RtpPacket::Header *header,
	uint8_t** ptr, size_t len, size_t& readed_len) {
	HI_FUNC_DEBUG();
	if (0u == header->extension) {
		return nullptr;
	}

	// The extension header is at least 4 bytes.
	static const  size_t EXTESION_ELN_EXCLUDE_VALUE = 4;
	if (len < (static_cast<size_t>(readed_len) + EXTESION_ELN_EXCLUDE_VALUE)) {
		writeErrror("not enough space for the announced extension header, packet discarded");
		return nullptr;
	}

	// Check header extension.
	RtpPacket::ExtensionHeader* extensionHeader = reinterpret_cast<RtpPacket::ExtensionHeader*>(*ptr);

	// The header extension contains a 16-bit length field that counts the
	// number of 32-bit words in the extension, excluding the four-octet
	// extension header.
	size_t extensionValueSize = static_cast<size_t>(ntohs(extensionHeader->length) * 4);

	// Packet size must be >= header size + CSRC list + header extension size.
	if (len < readed_len + 4 + extensionValueSize) {
		writeErrror("not enough space for the announced header extension value, packet discarded");
		return nullptr;
	}
	*ptr += 4 + extensionValueSize;
	readed_len += 4 + extensionValueSize;
	return extensionHeader;
}

static RtpPacket* createRtpPacket(RtpPacket::Header *header, RtpPacket::ExtensionHeader *extensionHeader,
	uint8_t *payload, bool parsePadding, size_t len, size_t readed_len) {
	size_t payloadLength = len - readed_len;
	uint8_t payloadPadding{0};
	auto packet = new RtpPacket(header, extensionHeader, payload, payloadLength, payloadPadding, len);

	if (parsePadding && !packet->ParsePadding()) {
		writeErrror("parse padding failed,  packet discarded");
		delete packet;
		return nullptr;
	}
	return packet;
}

static bool IsRtp(const uint8_t *data, size_t len)	{
	auto header = const_cast<RtpPacket::Header*>(reinterpret_cast<const RtpPacket::Header*>(data));

	if (nullptr == header) {
		return false;
	}

	return ((len >= sizeof(RtpPacket::Header)) &&
		// DOC: https://tools.ietf.org/html/draft-ietf-avtcore-rfc5764-mux-fixes
		(data[0] > 127 && data[0] < 192) &&
		// RTP Version must be 2.
		(header->version == 2));
}
} // namespace Hi
